#ifndef ZDATAVIEW_INL
#define ZDATAVIEW_INL
#include "ZDataView.h"

//////////////////////////////////////////////////////////////////////////////
////					对应字段值： 对df.txt df.txt_sep进行字符拼接
//////////////////////////////////////////////////////////////////////////////
template<typename StringType>
StringType NDFrame::ZDataView<StringType>::join(Field field_, const StringType& Char_) const {
	if (!(field_ == Field::TEXT || field_ == Field::TEXT_SEP)) {
		throw std::invalid_argument("Field must be TEXT or TEXT_SEP");
	}
	if (this->empty() || Char_.size() >= 2) {
		return {};
	}

	std::function<const StringType& (const ItemType&)> getField;
	if (field_ == Field::TEXT) {
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
	}
	else if (field_ == Field::TEXT_SEP) {
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
	}

	std::basic_ostringstream<CharType> oss;
	for (size_t i = 0; i < m_size; ++i) {
		if (i != 0 && !Char_.empty()) {
			oss << Char_;
		}
		oss << getField(m_dataPtr[i]);
	}
	return oss.str();
}


/////////////////////////////////////////////////////////////////////////////////////////
////             对应字段值： 对df.txt df.txt_sep进行字符拼接并替换字符(oldChar->newChar)
/////////////////////////////////////////////////////////////////////////////////////////
template<typename StringType>
StringType NDFrame::ZDataView<StringType>::join_replace(Field field_, CharType oldChar, CharType newChar,
	const StringType& Char_) const {
	if (!(field_ == Field::TEXT || field_ == Field::TEXT_SEP)) {
		throw std::invalid_argument("Field must be TEXT or TEXT_SEP");
	}
	if (this->empty() || Char_.size() >= 2) {
		return {};
	}

	std::function<const StringType& (const ItemType&)> getField;
	if (field_ == Field::TEXT) {
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
	}
	else if (field_ == Field::TEXT_SEP) {
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
	}

	std::basic_ostringstream<CharType> oss;
	for (size_t i = 0; i < m_size; ++i) {
		if (i != 0 && !Char_.empty()) {
			oss << Char_;
		}
		for (auto c : getField(m_dataPtr[i])) {
			if (c == oldChar) {
				oss << newChar;
			}
			else {
				oss << c;
			}
		}
	}

	return oss.str();
}



template<typename StringType>
StringType
NDFrame::ZDataView<StringType>::join_replace2(Field field_, CharType oldChar0, CharType newChar0, CharType oldChar1,
	CharType newChar1, const StringType& Char_) const {
	if (!(field_ == Field::TEXT || field_ == Field::TEXT_SEP)) {
		throw std::invalid_argument("Field must be TEXT or TEXT_SEP");
	}
	if (this->empty() || Char_.size() >= 2) {
		return {};
	}
	std::function<const StringType& (const ItemType&)> getField;
	if (field_ == Field::TEXT) {
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
	}
	else if (field_ == Field::TEXT_SEP) {
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
	}

	std::basic_ostringstream<CharType> oss;
	for (size_t i = 0; i < m_size; ++i) {
		if (i != 0 && !Char_.empty()) {
			oss << Char_;
		}
		for (auto c : getField(m_dataPtr[i])) {
			if (c == oldChar0) {
				oss << newChar0;
			}
			else if (c == oldChar1) {
				oss << newChar1;
			}
			else {
				oss << c;
			}
		}
	}
	return oss.str();
}


//////////////////////////////////////////////////////////////////////////////
////               通过索引(索引可能不连续)生成一个全新副本的ZDataFrame
//////////////////////////////////////////////////////////////////////////////
template<typename StringType>
NDFrame::ZDataFrame<StringType>
NDFrame::ZDataView<StringType>::DF_indices_to_df(const std::vector<size_t>& indices) const {
	if (indices.empty()) {
		throw std::out_of_range("indices is empty");
	}

	std::unordered_set<size_t> indices_set(indices.begin(), indices.end());
	ZDataFrame<StringType> newFrame(indices.size());

	size_t j = 0;
	for (size_t i = 0; i < m_size; ++i) {
		if (indices_set.find(m_dataPtr[i].idx) != indices_set.end()) {
			newFrame.getItemPtr()[j] = m_dataPtr[i];
			newFrame.getItemPtr()[j].idx = static_cast<int>(j);
			j++;
		}
	}

	newFrame.set_size(j);
	return newFrame;
}


//////////////////////////////////////////////////////////////////////////////
////               通过指针(地址可能不连续)生成一个全新副本的ZDataFrame
//////////////////////////////////////////////////////////////////////////////
template<typename StringType>
NDFrame::ZDataFrame<StringType> NDFrame::ZDataView<StringType>::DF_pointers_to_df(const VecItemPtr& pointers) const {
	if (pointers.empty()) {
		throw std::out_of_range("pointers is empty");
	}

	std::unordered_set<ItemType*> pointers_set(pointers.begin(), pointers.end());
	ZDataFrame<StringType> newFrame(pointers.size());

	size_t j = 0;
	for (size_t i = 0; i < m_size; ++i) {
		if (pointers_set.find(&m_dataPtr[i]) != pointers_set.end()) {
			newFrame.getItemPtr()[j] = m_dataPtr[i];
			newFrame.getItemPtr()[j].idx = static_cast<int>(j); /* 更新新DataFrame中的索引 */
			j++;
		}
	}

	newFrame.set_size(j);
	return newFrame;
}


//////////////////////////////////////////////////////////////////////////////
////      对应字段值： 对df.txt df.txt_sep是否在vector<field>容器内
//////////////////////////////////////////////////////////////////////////////

template<typename StringType>
bool NDFrame::ZDataView<StringType>::isin(const std::vector<StringType>& field,
	NDFrame::ZDataFrame<StringType>& out_dataframe) const {
	std::vector<size_t> indices;
	bool result = isin(field, indices);
	if (result) {
		out_dataframe = DF_indices_to_df(indices);
	}
	return result;
}


//////////////////////////////////////////////////////////////////////////////
////				   通用函数 对应字段值进行过滤
//////////////////////////////////////////////////////////////////////////////
template<typename StringType>
bool NDFrame::ZDataView<StringType>::filter(const std::function<bool(const ItemType&)>& predicate,
	std::vector<DataItem<StringType>*>& out_pointers) const {
	if (!out_pointers.empty()) { out_pointers.clear(); }
	out_pointers.reserve((int)(static_cast<double>(this->size()) * 0.5) + 2);
	for (size_t i = 0; i < m_size; ++i) {
		if (predicate(m_dataPtr[i])) {
			out_pointers.push_back(&m_dataPtr[i]);
		}
	}
	out_pointers.shrink_to_fit();
	return !out_pointers.empty();
}


//////////////////////////////////////////////////////////////////////////////
////				   通用函数 对应字段值进行过滤(返回dataframe副本)
//////////////////////////////////////////////////////////////////////////////
template<typename StringType>
NDFrame::ZDataFrame<StringType>
NDFrame::ZDataView<StringType>::DF_filter(const std::function<bool(const ItemType&)>& predicate) const {
	ZDataFrame<StringType> result(m_size > 3 ? 3 : m_size);
	if (m_size == 0) { return result; }
	for (size_t i = 0; i < m_size; ++i) {
		if (predicate(m_dataPtr[i])) {
			result.push_back(m_dataPtr[i]);
		}
	}
	return result;
}


//////////////////////////////////////////////////////////////////////////////
////			   专用函数 对应字段值进行过滤--返回df副本（深复制）
//////////////////////////////////////////////////////////////////////////////
template<typename StringType>
NDFrame::ZDataFrame<StringType> NDFrame::ZDataView<StringType>::DF_reg_common4(
	NDFrame::Field field,
	const std::vector<RegexType>& regex_, int len,
	const std::function<bool(size_t, size_t)>& compare) const {
	ZDataFrame<StringType> result(m_size > 3 ? 3 : m_size);
	if (m_size == 0) { return result; }

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	for (size_t i = 0; i < m_size; ++i) {
		const StringType& field_value = getField(m_dataPtr[i]);
		if (compare(field_value.size(), len)) {
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					result.push_back(m_dataPtr[i]);
					break;
				}
			}
		}
	}
	return result;
}


template<typename StringType>
NDFrame::ZDataFrame<StringType> NDFrame::ZDataView<StringType>::DF_reg_common3(
	NDFrame::Field field,
	const std::vector<RegexType>& regex_,
	const std::function<bool(const ItemType&)>& condition
) const {
	ZDataFrame<StringType> result(m_size > 3 ? 3 : m_size);
	if (m_size == 0) { return result; }

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	for (size_t i = 0; i < m_size; ++i) {
		if (condition(m_dataPtr[i])) {
			const StringType& field_value = getField(m_dataPtr[i]);
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					result.push_back(m_dataPtr[i]);
					break;
				}
			}
		}
	}
	return result;
}


template<typename StringType>
NDFrame::ZDataFrame<StringType>
NDFrame::ZDataView<StringType>::DF_reg_impl(NDFrame::Field field, const std::vector<RegexType>& regex_) const {
	if (!(field == Field::TEXT || field == Field::TEXT_SEP)) {
		throw std::invalid_argument("Field must be TEXT or TEXT_SEP");
	}
	ZDataFrame<StringType> result(m_size > 3 ? 3 : m_size);
	if (m_size == 0) { return result; }

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	for (size_t i = 0; i < m_size; ++i) {
		for (const auto& reg : regex_) {
			const StringType& field_value = getField(m_dataPtr[i]);
			if (matches_regex(field_value, reg)) {
				result.push_back(m_dataPtr[i]);
				break;
			}
		}
	}

	return result;
}


template<typename StringType>
NDFrame::ZDataFrame<StringType> NDFrame::ZDataView<StringType>::DF_xyxy_impl(
	const std::function<bool(const ItemType&)>& condition) const {
	ZDataFrame<StringType> result(m_size > 3 ? 3 : m_size);
	for (size_t i = 0; i < m_size; ++i) {
		if (condition(m_dataPtr[i])) {
			result.push_back(m_dataPtr[i]);
		}
	}
	return result;
}


////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
///						 ZDataView 类的过滤函数		                ////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////

template<typename StringType>
bool NDFrame::ZDataView<StringType>::ptr2_reg_impl(VecItemPtr& out_ptr, Field field,
	const std::vector<RegexType>& regex_) const {
	if (!(field == Field::TEXT || field == Field::TEXT_SEP)) {
		throw std::invalid_argument("Field must be TEXT or TEXT_SEP");
	}
	if (out_ptr.empty()) { out_ptr.reserve(3); };

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	for (size_t i = 0; i < m_size; ++i) {
		const StringType& field_value = getField(m_dataPtr[i]);
		for (const auto& reg : regex_) {
			if (matches_regex(field_value, reg)) {
				out_ptr.push_back(&m_dataPtr[i]);
				break;
			}
		}

	}
	return !out_ptr.empty();
}


template<typename StringType>
bool NDFrame::ZDataView<StringType>::ptr2_reg_common5(
	VecItemPtr& out_ptr,
	Field field,
	const std::vector<RegexType>& regex_, int len,
	const std::function<bool(size_t, size_t)>& compare) const {

	if (out_ptr.empty()) { out_ptr.reserve(3); };

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	for (size_t i = 0; i < m_size; ++i) {
		const StringType& field_value = getField(m_dataPtr[i]);
		if (compare(field_value.size(), len)) {
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					out_ptr.push_back(&m_dataPtr[i]);
					break;
				}
			}
		}
	}
	return !out_ptr.empty();
}


template<typename StringType>
bool NDFrame::ZDataView<StringType>::ptr2_reg_common4(
	VecItemPtr& out_ptr,
	NDFrame::Field field,
	const RegexType& regex_,
	const std::function<bool(const DataItem<StringType>&)>& condition
) const {

	if (out_ptr.empty()) { out_ptr.reserve(3); };

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	for (size_t i = 0; i < m_size; ++i) {
		const StringType& field_value = getField(m_dataPtr[i]);
		if (condition(m_dataPtr[i]) && matches_regex(field_value, regex_)) {
			out_ptr.push_back(&m_dataPtr[i]);
		}
	}

	return !out_ptr.empty();
}


template<typename StringType>
bool NDFrame::ZDataView<StringType>::ptr2_xyxy(VecItemPtr& out_ptr,
	const std::function<bool(const DataItem<StringType>&)>& condition) const {
	if (out_ptr.empty()) { out_ptr.reserve(3); };

	for (size_t i = 0; i < m_size; ++i) {
		if (condition(m_dataPtr[i])) {
			out_ptr.push_back(&m_dataPtr[i]);
		}
	}

	return !out_ptr.empty();
}


//////////////////////////////////////////////////////////////////////////////
////		   对 ZDataView 进行二次过滤（二次筛选函数：传入第一次筛选的索引）返回视图
//////////////////////////////////////////////////////////////////////////////
template<typename StringType>
NDFrame::ZDataView<StringType>
NDFrame::ZDataView<StringType>::dv2_reg_impl(Field field, const std::vector<RegexType>& regex_) const {

	if (m_size == 0) { return ZDataView{ *this, 0, 0 }; }

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	size_t startIndex = m_size;
	size_t endIndex = 0;

	for (size_t i = 0; i < m_size; ++i) {
		const StringType& field_value = getField(m_dataPtr[i]);
		bool matched = false;
		for (const auto& reg : regex_) {
			if (matches_regex(field_value, reg)) {
				matched = true;
				break;
			}
		}

		if (matched) {
			if (startIndex == m_size) {
				startIndex = i;
			}
			endIndex = i + 1;
		}
		else {
			if (startIndex != m_size && endIndex > 0) {
				return ZDataView{ *this, startIndex, endIndex };
			}
			startIndex = m_size;
			endIndex = 0;
		}
	}

	if (startIndex != m_size && endIndex > 0) {
		return ZDataView{ *this, startIndex, endIndex };
	}
	return ZDataView{ *this, 0, 0 };
}


template<typename StringType>
NDFrame::ZDataView<StringType> NDFrame::ZDataView<StringType>::dv2_reg_common4(
	NDFrame::Field field,
	const std::vector<RegexType>& regex_, int len,
	const std::function<bool(size_t, size_t)>& compare) const {

	if (m_size == 0) { return ZDataView{ *this, 0, 0 }; }

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	size_t startIndex = m_size;
	size_t endIndex = 0;

	for (size_t i = 0; i < m_size; ++i) {
		const StringType& field_value = getField(m_dataPtr[i]);
		if (compare(field_value.size(), len)) {
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					if (startIndex == m_size) {
						startIndex = i;
					}
					endIndex = i + 1;
					break;
				}
			}
			if (startIndex != m_size && endIndex > 0) {
				return ZDataView{ *this, startIndex, endIndex };
			}
		}
		else {
			if (startIndex != m_size && endIndex > 0) {
				return ZDataView{ *this, startIndex, endIndex };
			}
			startIndex = m_size;
			endIndex = 0;
		}
	}

	if (startIndex != m_size && endIndex > 0) {
		return ZDataView{ *this, startIndex, endIndex };
	}
	return ZDataView{ *this, 0, 0 };
}


template<typename StringType>
NDFrame::ZDataView<StringType> NDFrame::ZDataView<StringType>::dv2_reg_common3(
	NDFrame::Field field,
	const std::vector<RegexType>& regex_,
	const std::function<bool(const DataItem<StringType>&)>& condition
) const {

	if (m_size == 0) { return ZDataView{ *this, 0, 0 }; }

	std::function<const StringType& (const ItemType&)> getField;

	switch (field) {
	case Field::TEXT:
		getField = [](const ItemType& item) -> const StringType& { return item.text; };
		break;
	case Field::TEXT_SEP:
		getField = [](const ItemType& item) -> const StringType& { return item.text_sep; };
		break;
	default:
		throw std::invalid_argument("Invalid field");
	}

	size_t startIndex = m_size;
	size_t endIndex = 0;

	for (size_t i = 0; i < m_size; ++i) {
		const StringType& field_value = getField(m_dataPtr[i]);
		if (condition(m_dataPtr[i])) {
			for (const auto& reg : regex_) {
				if (matches_regex(field_value, reg)) {
					if (startIndex == m_size) {
						startIndex = i;
					}
					endIndex = i + 1;
					break;
				}
			}
			if (startIndex != m_size && endIndex > 0) {
				return ZDataView{ *this, startIndex, endIndex };
			}
		}
		else {
			if (startIndex != m_size && endIndex > 0) {
				return ZDataView{ *this, startIndex, endIndex };
			}
			startIndex = m_size;
			endIndex = 0;
		}
	}

	if (startIndex != m_size && endIndex > 0) {
		return ZDataView{ *this, startIndex, endIndex };
	}

	return ZDataView{ *this, 0, 0 };
}



//////////////////////////////////////////////////////////////////////////


template<typename StringType>
template<typename DVConditionIdx>
NDFrame::ZDataView<StringType> NDFrame::ZDataView<StringType>::dv2_loc(DVConditionIdx conditionIdx) const {
	if (m_size == 0) return ZDataView{ *this, 0, 0 };

	size_t startIndex = m_size;
	size_t endIndex = 0;

	for (size_t i = 0; i < m_size; ++i) {
		if (conditionIdx(m_dataPtr[i])) {
			if (startIndex == m_size) {
				startIndex = i;
			}
			endIndex = i + 1;
		}
		else {
			if (startIndex != m_size && endIndex > 0) {
				return ZDataView{ *this, startIndex, endIndex };
			}
			startIndex = m_size;
			endIndex = 0;
		}
	}

	if (startIndex != m_size && endIndex > 0) {
		return ZDataView{ *this, startIndex, endIndex };
	}

	return ZDataView{ *this, 0, 0 };
}


#endif